package com.sneakxy.mybatis.commons.orm.mapping;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.reflect.FieldUtils;
import org.apache.ibatis.type.TypeHandlerRegistry;
import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.ReadOnlyProperty;
import org.springframework.data.annotation.Transient;

import com.sneakxy.mybatis.commons.annotations.Column;
import com.sneakxy.mybatis.commons.utils.MybatisUtils;

/**
 * @author 潜行的青衣
 */
public class MybatisColumn {

	private String property;
	
	private String column;
	
	private String propertyPath;
	
	private boolean readonly;
	
	private boolean primary;
	
	public MybatisColumn(String property, String column) {
		this(property, column, property, false, false);
	}
	
	public MybatisColumn(String property, String column, String propertyPath, boolean primary, boolean readonly) {
		this.property = property;
		this.column = column;
		this.propertyPath = propertyPath;
		this.primary = primary;
		this.readonly = readonly;
	}

	public String getProperty() {
		return property;
	}

	public String getPropertyPath() {
		return propertyPath;
	}

	public String getColumn() {
		return column;
	}

	public boolean isPrimary() {
		return primary;
	}
	
	public boolean isReadonly() {
		return readonly;
	}

	public static List<MybatisColumn> getMybatisColumns(TypeHandlerRegistry typeHandlerRegistry, Class<?> loader) {
		return getMybatisColumns(typeHandlerRegistry, loader, "");
	}
	
	public static List<MybatisColumn> getMybatisColumns(TypeHandlerRegistry typeHandlerRegistry, Class<?> loader, String path) {
		List<MybatisColumn> columns = new ArrayList<MybatisColumn>();
		String prefix = new StringBuilder().append(path).append(StringUtils.isBlank(path) ? "" : ".").toString();
		final Field[] fields = FieldUtils.getAllFields(loader);
		for(Field field: fields) {
			if(Modifier.isStatic(field.getModifiers())) {
				continue;
			}
			columns.addAll(getMybatisColumns(typeHandlerRegistry, loader, field, prefix));
		}
		return columns;
	}
	
	private static List<MybatisColumn> getMybatisColumns(TypeHandlerRegistry typeHandlerRegistry, Class<?> loader, Field field, String prefix) {
		List<MybatisColumn> columns = new ArrayList<MybatisColumn>();
		if(Modifier.isStatic(field.getModifiers())) {
			return columns;
		}
		if(MybatisUtils.getAnnotation(loader, field, Transient.class) != null) {
			return columns;
		}
		boolean primary = MybatisUtils.getAnnotation(loader, field, Id.class) != null;
		String propertyPath = new StringBuilder().append(prefix).append(field.getName()).toString();
		if(typeHandlerRegistry.hasTypeHandler(field.getType())) {
			columns.add(
					new MybatisColumn(
						field.getName(), 
						getColumnName(loader, field), 
						propertyPath,
						primary, 
						field.getAnnotation(ReadOnlyProperty.class) != null));
		} else {
			List<MybatisColumn> subColumns = getMybatisColumns(typeHandlerRegistry, field.getType(), propertyPath);
			if(primary) {
				for(MybatisColumn subColumn : subColumns) {
					subColumn.primary = true;
				}
			}
			columns.addAll(subColumns);
		}
		return columns;
	}
	
	private static String getColumnName(Class<?> loader, Field field) {
		Column column = MybatisUtils.getAnnotation(loader, field, Column.class);
		if(column != null) {
			if(StringUtils.isNotBlank(column.value())) {
				return column.value();
			}
		}
		return MybatisUtils.toSqlColumnName(field.getName());
	}
	
	public static List<MybatisColumn> getColumns(TypeHandlerRegistry typeHandlerRegistry, Class<?> domainClass, String property) {
		return getColumns(typeHandlerRegistry, domainClass, property, "");
	}
	
	public static List<MybatisColumn> getColumns(TypeHandlerRegistry typeHandlerRegistry, Class<?> domainClass, String property, String path) {
		Field field = MybatisUtils.getField(domainClass, property);
		String prefix = new StringBuilder().append(path).append(StringUtils.isBlank(path) ? "" : ".").toString();
		return getMybatisColumns(typeHandlerRegistry, domainClass, field, prefix);
	}
	
}